﻿using System;
using System.Runtime.InteropServices;
using EnvDTE;
using EnvDTE80;
using Extensibility;
using MEFAddin.Adapters;
using MEFAddin.Interfaces;

namespace MEFAddin.Controllers
{
    public class AddInController : IAddInController
    {
        private string _uiDLL;

        private IAddInAdapter _AddinAdapter;

        private AddInContext _AddInContext;

        /// <summary>
        /// Gets the add in instance.
        /// </summary>
        public EnvDTE.AddIn AddInInstance
        {
            get
            {
                return _AddInContext.AddInInstance;
            }
        }

        /// <summary>
        /// Gets the application object.
        /// </summary>
        public DTE2 ApplicationObject
        {
            get
            {
                return _AddInContext.ApplicationObject;
            }
        }

        /// <summary>
        /// Occurs when [on before execute].
        /// </summary>
        public event _dispCommandEvents_BeforeExecuteEventHandler OnBeforeExecute;

        /// <summary>
        /// Occurs when [on after execute].
        /// </summary>
        public event _dispCommandEvents_AfterExecuteEventHandler OnAfterExecute;

        /// <summary>
        /// Gets the addin adapter.
        /// </summary>
        public IAddInAdapter AddinAdapter
        {
            get
            {
                return this._AddinAdapter;
            }
        }

        /// <summary>
        /// Gets the UI DLL.
        /// </summary>
        public string UIDll
        {
            get
            {
                return _uiDLL;
            }
        }

        /// <summary>
        /// Gets the add in adapter.
        /// </summary>
        public IAddInAdapter AddInAdapter
        {
            get { return this._AddinAdapter; }
        }

        /// <summary>
        /// Gets or sets the common UI assembly location.
        /// </summary>
        /// <value>
        /// The common UI assembly location.
        /// </value>
        public string CommonUIAssemblyLocation { get; set; }

        [DllImport("user32.dll")]
        public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

        /// <summary>
        /// Initializes the specified addin CTX.
        /// </summary>
        /// <param name="addinCtx">The addin CTX.</param>
        /// <param name="addinAdapter">The addin adapter.</param>
        public void Initialize(AddInContext addinCtx, IAddInAdapter addinAdapter)
        {
            _AddInContext = addinCtx;
            this._AddinAdapter = addinAdapter;
            addinAdapter.CommandAdapter.CommandEvents.AfterExecute += new _dispCommandEvents_AfterExecuteEventHandler(_CommandEvents_AfterExecute);
            addinAdapter.CommandAdapter.CommandEvents.BeforeExecute += new _dispCommandEvents_BeforeExecuteEventHandler(_CommandEvents_BeforeExecute);
        }

        /// <summary>
        /// Locks the around.
        /// </summary>
        /// <param name="lockObject">The lock object.</param>
        /// <param name="callback">The callback.</param>
        /// <param name="arg">The arg.</param>
        public void LockAround(object lockObject, Action<object> callback, object arg)
        {
            lock (lockObject)
            {
                callback(arg);
            }
        }

        /// <summary>
        /// Called when [connection].
        /// </summary>
        /// <param name="application">The application.</param>
        /// <param name="connectMode">The connect mode.</param>
        /// <param name="addInInst">The add in inst.</param>
        /// <param name="custom">The custom.</param>
        public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
        {
            try
            {
                this._AddinAdapter.LoggingAdapter.InitLogWindow();
                InternalOnConnectionCompleted();
                this._AddinAdapter.LoggingAdapter.LogMessage(string.Format("Controller [{0}] Connected", this.GetType().FullName));
            }
            catch (Exception e)
            {
                this._AddinAdapter.LoggingAdapter.LogError("Error AddInController.OnConnection: " + e.ToString());
            }
        }

        /// <summary>
        /// Called when [disconnection].
        /// </summary>
        /// <param name="disconnectMode">The disconnect mode.</param>
        /// <param name="custom">The custom.</param>
        public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
        {
            this.InternalOnDisconnectionCompleted(disconnectMode, ref custom);

        }

        public void Exec(string CmdName, vsCommandExecOption ExecuteOption, ref object VariantIn, ref object VariantOut, ref bool Handled)
        {
            this.InternalExec(CmdName, ExecuteOption, ref VariantIn, ref VariantOut, ref Handled);


        }

        public void QueryStatus(string CmdName, vsCommandStatusTextWanted NeededText, ref vsCommandStatus StatusOption, ref object CommandText)
        {

            if (NeededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
            {
                this.InternalQueryStatus(CmdName, NeededText, ref StatusOption, ref CommandText);
            }
            StatusOption = vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
            //CommandText = "TestCommandText";
        }



        /// <summary>
        /// Called when [startup complete].
        /// </summary>
        /// <param name="custom">The custom.</param>
        public void OnStartupComplete(ref Array custom)
        {
            try
            {
                this._AddinAdapter.LoggingAdapter.ShowLogWindow();
            }
            catch (Exception e)
            {
                this._AddinAdapter.LoggingAdapter.LogError("Error OnStartupComplete: " + e.ToString());
            }
            this.InternalOnStartupCompleteCompleted(ref custom);

        }

        /// <summary>
        /// Called when [add ins update].
        /// </summary>
        /// <param name="custom">The custom.</param>
        public void OnAddInsUpdate(ref Array custom)
        {
            this.InternalOnAddInsUpdateCompleted(ref custom);
        }

        /// <summary>
        /// Called when [begin shutdown].
        /// </summary>
        /// <param name="custom">The custom.</param>
        public void OnBeginShutdown(ref Array custom)
        {
            this.InternalOnBeginShutdownCompleted(ref custom);
        }

        /// <summary>
        /// Gets the logging adapter.
        /// </summary>
        protected ILoggingAdapter LoggingAdapter
        {
            get
            {
                return this._AddinAdapter.LoggingAdapter;
            }
        }

        /// <summary>
        /// Gets the working directory.
        /// </summary>
        protected string WorkingDirectory
        {
            get
            {
                return _AddInContext.WorkingDirectory;
            }
        }

        /// <summary>
        /// Gets the window adapter.
        /// </summary>
        protected IWindowAdapter WindowAdapter
        {
            get
            {
                return this._AddinAdapter.WindowAdapter;
            }
        }

        /// <summary>
        /// Gets the add in context.
        /// </summary>
        protected AddInContext AddInContext
        {
            get
            {
                return this._AddInContext;
            }
        }

        /// <summary>
        /// Internals the command events_ after execute.
        /// </summary>
        /// <param name="Guid">The GUID.</param>
        /// <param name="ID">The ID.</param>
        /// <param name="CustomIn">The custom in.</param>
        /// <param name="CustomOut">The custom out.</param>
        protected virtual void InternalCommandEvents_AfterExecute(string Guid, int ID, object CustomIn, object CustomOut)
        {
        }

        /// <summary>
        /// Internals the command events_ before execute.
        /// </summary>
        /// <param name="Guid">The GUID.</param>
        /// <param name="ID">The ID.</param>
        /// <param name="CustomIn">The custom in.</param>
        /// <param name="CustomOut">The custom out.</param>
        /// <param name="CancelDefault">if set to <c>true</c> [cancel default].</param>
        protected virtual void InternalCommandEvents_BeforeExecute(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault)
        {
        }

        /// <summary>
        /// Internals the on connection completed.
        /// </summary>
        protected virtual void InternalOnConnectionCompleted()
        {
        }

        /// <summary>
        /// Internals the on disconnection completed.
        /// </summary>
        /// <param name="disconnectMode">The disconnect mode.</param>
        /// <param name="custom">The custom.</param>
        protected virtual void InternalOnDisconnectionCompleted(ext_DisconnectMode disconnectMode, ref Array custom)
        {
        }

        /// <summary>
        /// _s the command events_ after execute.
        /// </summary>
        /// <param name="Guid">The GUID.</param>
        /// <param name="ID">The ID.</param>
        /// <param name="CustomIn">The custom in.</param>
        /// <param name="CustomOut">The custom out.</param>
        private void _CommandEvents_AfterExecute(string Guid, int ID, object CustomIn, object CustomOut)
        {
            InternalCommandEvents_AfterExecute(Guid, ID, CustomIn, CustomOut);
        }

        /// <summary>
        /// _s the command events_ before execute.
        /// </summary>
        /// <param name="Guid">The GUID.</param>
        /// <param name="ID">The ID.</param>
        /// <param name="CustomIn">The custom in.</param>
        /// <param name="CustomOut">The custom out.</param>
        /// <param name="CancelDefault">if set to <c>true</c> [cancel default].</param>
        private void _CommandEvents_BeforeExecute(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault)
        {
            InternalCommandEvents_BeforeExecute(Guid, ID, CustomIn, CustomOut, ref CancelDefault);
        }


        /// <summary>
        /// Internals the on add ins update completed.
        /// </summary>
        /// <param name="custom">The custom.</param>
        protected virtual void InternalOnAddInsUpdateCompleted(ref Array custom)
        {
        }

        /// <summary>
        /// Internals the on startup complete completed.
        /// </summary>
        /// <param name="custom">The custom.</param>
        protected virtual void InternalOnStartupCompleteCompleted(ref Array custom)
        {
        }

        /// <summary>
        /// Internals the on begin shutdown completed.
        /// </summary>
        /// <param name="custom">The custom.</param>
        protected virtual void InternalOnBeginShutdownCompleted(ref Array custom)
        {
        }

        /// <summary>
        /// Internals the exec.
        /// </summary>
        /// <param name="CmdName">Name of the CMD.</param>
        /// <param name="ExecuteOption">The execute option.</param>
        /// <param name="VariantIn">The variant in.</param>
        /// <param name="VariantOut">The variant out.</param>
        /// <param name="Handled">if set to <c>true</c> [handled].</param>
        protected virtual void InternalExec(string CmdName, vsCommandExecOption ExecuteOption, ref object VariantIn, ref object VariantOut, ref bool Handled)
        {
        }

        /// <summary>
        /// Internals the query status.
        /// </summary>
        /// <param name="CmdName">Name of the CMD.</param>
        /// <param name="NeededText">The needed text.</param>
        /// <param name="StatusOption">The status option.</param>
        /// <param name="CommandText">The command text.</param>
        protected virtual void InternalQueryStatus(string CmdName, vsCommandStatusTextWanted NeededText, ref vsCommandStatus StatusOption, ref object CommandText)
        {
        }
    }
}
